﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Windows.Forms;

namespace Extensil.Processor.Extensibility
{
    public class PluginAssembly
    {
        static Dictionary<string, Assembly> _LoadedAssembly = new Dictionary<string, Assembly>();
        public static Assembly Load(string assemblyPath)
        {
            
            Assembly asm = null;
            FileInfo fInfo = new FileInfo(assemblyPath);
            if(_LoadedAssembly.ContainsKey(fInfo.FullName.ToLower()))
                return _LoadedAssembly[fInfo.FullName.ToLower()];
            using(FileStream fs = new FileStream(fInfo.FullName, FileMode.Open, FileAccess.Read))
            {
                using(MemoryStream ms = new MemoryStream())
                {
                    byte[] b = new byte[4096];
                    while(fs.Read(b, 0, b.Length) > 0)
                    {
                        ms.Write(b, 0, b.Length);
                    }
                    asm = Assembly.Load(ms.ToArray());
                    _LoadedAssembly.Add(fInfo.FullName.ToLower(), asm);
                }
            }
            return asm;
        }
        public static T GetHandler<T>(string handler)
        {
            string[] catClass = handler.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            string hpath = PluginAssembly.GetPluginHandler(catClass[1].Trim());
            if(String.IsNullOrEmpty(hpath))
                throw new Exception("Handler " + catClass[1].Trim() + " could not be found.");
            try
            {
                Assembly asm = PluginAssembly.Load(hpath);
                Type type = asm.GetType(catClass[0].Trim());
                if(type == null)
                    throw new Exception("No class found with name '" + catClass[0].Trim() + "' in " + catClass[1].Trim() + ".");
             
                return (T)asm.CreateInstance(catClass[0].Trim());
            }
            catch(Exception exc)
            {
                throw new Exception("Invalid handler information. " + LinkedException(exc));
            }
        }
        public static bool HasAncestorBaseType(Type type, Type baseType)
        {
            if(type.BaseType == baseType)
                return true;
            if(type.BaseType == null)
                return false;
            return HasAncestorBaseType(type.BaseType, baseType);
        }
        public static string GetPluginHandler(string name)
        {
            foreach(string extension in new string[] { "dll", "exe" })
            {
                if(File.Exists(name + "." + extension))
                    return new FileInfo(name + "." + extension).FullName;
                FileInfo fInfo = new FileInfo(name + "." + extension);
                if(File.Exists(fInfo.FullName))
                    return fInfo.FullName;
                if(File.Exists(Application.StartupPath + "/" + name + "." + extension))
                    return Application.StartupPath + "/" + name + "." + extension;
                try
                {
                    string[] files = Directory.GetFiles(Application.StartupPath, name + "." + extension,
                        SearchOption.AllDirectories);
                    if(files.Length > 0)
                        return files[0];
                }
                catch(Exception exc)
                {
                }
            }
            
            return null;
        }
        static string LinkedException(Exception exc)
        {
            return exc.Message + (exc.InnerException == null ? "" : "\r\n\t" + LinkedException(exc.InnerException));
        }
    }
}
